/* p9_3: I2C to DS1307 Multi-byte burst read */

/* This program communicate with the DS1307 Real-time Clock via I2C. */
/* It reads all seven time/date registers using burst read. */

/* DS1307 parameters:
  fmax = 100 kHz

  I2C1SCL PA6
  I2C1SDA PA7 */

#include "TM4C123GH6PM.h"

#define SLAVE_ADDR 0x68     /* 0110 1000 */

void I2C1_init(void);
char I2C1_read(int slaveAddr, char memAddr, int byteCount, char* data);

int main(void)
{
    char timeDateReadBack[7];

    I2C1_init();

    for (;;)
    {
        /* use burst read to read time and date */
        I2C1_read(SLAVE_ADDR, 0, 7, timeDateReadBack);
        /* display date time here */
    }
}

/* initialize I2C1 as master and the port pins */
void I2C1_init(void)
{
    SYSCTL->RCGCI2C |= 0x02;    /* enable clock to I2C1 */
    SYSCTL->RCGCGPIO |= 0x01;   /* enable clock to GPIOA */

    /* PORTA 7, 6 for I2C1 */
    GPIOA->AFSEL |= 0xC0;       /* PORTA 7, 6 for I2C1 */
    GPIOA->PCTL &= ~0xFF000000; /* PORTA 7, 6 for I2C1 */
    GPIOA->PCTL |= 0x33000000;
    GPIOA->DEN |= 0xC0;         /* PORTA 7, 6 as digital pins */
    GPIOA->ODR |= 0x80;         /* PORTA 7 as open drain */

    I2C1->MCR = 0x10;           /* master mode */
    I2C1->MTPR = 7;             /* 100 kHz @ 16 MHz */
}

/* This function is called by the startup assembly */
/* code to perform system specific initialization tasks. */
void SystemInit(void)
{
    /* Grant coprocessor access */
    /* This is required since TM4C123G has a floating point coprocessor */
    SCB->CPACR |= 0x00f00000;
}

/* Wait until I2C master is not busy and return error code */
/* If there is no error, return 0 */
static int I2C_wait_till_done(void)
{
    while(I2C1->MCS & 1);   /* wait until I2C master is not busy */
    return I2C1->MCS & 0xE; /* return I2C error code */
}

/* Read memory */
/* read: S-(saddr+w)-ACK-maddr-ACK-R-(saddr+r)-ACK-data-ACK-data-ACK-...-data-NACK-P */
char I2C1_read(int slaveAddr, char memAddr, int byteCount, char* data)
{
    char error;
    
    if (byteCount <= 0)
        return -1;         /* no read was performed */

    /* send slave address and starting address */
    I2C1->MSA = slaveAddr << 1;
    I2C1->MDR = memAddr;
    I2C1->MCS = 3;       /* S-(saddr+w)-ACK-maddr-ACK */
    error = I2C_wait_till_done();
    if (error)
        return error;

    /* to change bus from write to read, send restart with slave addr */
    I2C1->MSA = (slaveAddr << 1) + 1;   /* restart: -R-(saddr+r)-ACK */

    if (byteCount == 1)             /* if last byte, don't ack */
        I2C1->MCS = 7;              /* -data-NACK-P */
    else                            /* else ack */
        I2C1->MCS = 0xB;            /* -data-ACK- */
    error = I2C_wait_till_done();
    if (error) return error;

    *data++ = I2C1->MDR;            /* store the data received */

    if (--byteCount == 0)           /* if single byte read, done */
    {
        while(I2C1->MCS & 0x40);    /* wait until bus is not busy */
        return 0;       /* no error */
    }
 
    /* read the rest of the bytes */
    while (byteCount > 1)
    {
        I2C1->MCS = 9;              /* -data-ACK- */
        error = I2C_wait_till_done();
        if (error) return error;
        byteCount--;
        *data++ = I2C1->MDR;        /* store data received */
    }

    I2C1->MCS = 5;                  /* -data-NACK-P */
    error = I2C_wait_till_done();
    *data = I2C1->MDR;              /* store data received */
    while(I2C1->MCS & 0x40);        /* wait until bus is not busy */
    
    return 0;       /* no error */
}